#include "userprog/syscall.h"
#include <stdio.h>
#include <syscall-nr.h>
#include "threads/interrupt.h"
#include "threads/thread.h"

#include "process.h"
#include <threads/vaddr.h>
#include "pagedir.h"
#include <devices/shutdown.h>
#include "filesys/filesys.h"
#include "threads/malloc.h"
#include "filesys/file.h"
/* define by zhy */
#define STD_IN 0
#define STD_OUT 1

static void syscall_handler(struct intr_frame *);

/* define by rym*/
static int get_user(const uint8_t *uaddr);
void *check_ptr(const void *vaddr);
void *check_ptr_by_type(int *ptr);
void sys_halt(void);
void sys_exit(int status);
int sys_exec(const char *cmd_line);
int sys_wait(int pid);
bool sys_create(const char *filename, unsigned fileinitial_size);
bool sys_remove(const char *file);
int sys_open(const char *file);
int sys_filesize(int fd);
int sys_read(int fd, void *buffer, unsigned fdbuffersize);
int sys_write(int fd, const void *buffer, unsigned fdbuffersize);
void sys_seek(int fd, unsigned fdposition);
unsigned sys_tell(int fd);
void sys_close(int fd);
/* over */
/* define by zhy */
bool is_valid_string(const char *str);

bool is_valid_buffer(const char *buffer, const unsigned size);

struct file *get_file_by_fd(int fd, struct list_elem **e);

void syscall_init(void)
{
	intr_register_int(0x30, 3, INTR_ON, syscall_handler, "syscall");
}

/*
 根据中断类型的不同进行不同的系统调用
 realized by rym 
*/
static void
syscall_handler(struct intr_frame *f UNUSED)
{
	int *ptr = f->esp;

	check_ptr(ptr); /* edit by rym */
	check_ptr_by_type(ptr);

	int system_call = *ptr;

	switch (system_call)
	{
	case SYS_HALT:
		sys_halt();
		break;

	case SYS_EXIT:
		sys_exit(*(ptr + 1));
		break;

	case SYS_EXEC:
		f->eax = sys_exec(*(ptr + 1));
		break;

	case SYS_WAIT:
		f->eax = sys_wait(*(ptr + 1));
		break;

	case SYS_CREATE:
		f->eax = sys_create(*(ptr + 4), *(ptr + 5));
		break;

	case SYS_REMOVE:
		f->eax = sys_remove(*(ptr + 1));
		break;

	case SYS_OPEN:
		f->eax = sys_open(*(ptr + 1));
		break;

	case SYS_FILESIZE:
		f->eax = sys_filesize(*(ptr + 1));
		break;

	case SYS_READ:
		f->eax = sys_read(*(ptr + 5), *(ptr + 6), *(ptr + 7));
		break;

	case SYS_WRITE:
		f->eax = sys_write(*(ptr + 5), *(ptr + 6), *(ptr + 7));
		break;

	case SYS_SEEK:
		sys_seek(*(ptr + 4), *(ptr + 5));
		break;

	case SYS_TELL:
		f->eax = sys_tell(*(ptr + 1));
		break;

	case SYS_CLOSE:
		sys_close(*(ptr + 1));
		break;

	default:
		sys_exit(-1);
	}

}

/* 
根据中断类型检查指针
realized by rym 
*/
void *
check_ptr_by_type(int *ptr)
{
	int system_call = *ptr;

	switch (system_call)
	{
	case SYS_EXIT:
	case SYS_WAIT:
	case SYS_FILESIZE:
	case SYS_TELL:
	case SYS_CLOSE:
		check_ptr(ptr + 1);
		break;

	case SYS_EXEC:
	case SYS_REMOVE:
	case SYS_OPEN:
		check_ptr(ptr + 1);
		check_ptr(*(ptr + 1));
		break;

	case SYS_CREATE:
		check_ptr(ptr + 5);
		check_ptr(*(ptr + 4));
		break;

	case SYS_READ:
	case SYS_WRITE:
		check_ptr(ptr + 7);
		check_ptr(*(ptr + 6));
		break;

	case SYS_SEEK:
		check_ptr(ptr + 5);
		break;

	default:
		sys_exit(-1);
	}
}

static int
get_user(const uint8_t *uaddr)
{
	int result;
	asm("movl $1f, %0; movzbl %1, %0; 1:"
		: "=&a"(result)
		: "m"(*uaddr));
	return result;
}

/* 
检查指针
realized by rym 
*/
void *
check_ptr(const void *vaddr)
{
	/* 检查是否为空指针 */
	if (vaddr == NULL)
	{
		sys_exit(-1);
	}

	/* 检查是否是合法user指针 */
	if (!is_user_vaddr(vaddr))
	{
		sys_exit(-1);
	}
	/* 检查是否是有效user页 */
	void *ptr = pagedir_get_page(thread_current()->pagedir, vaddr);
	if (!ptr)
	{
		sys_exit(-1);
	}
	uint8_t *check_byteptr = (uint8_t *)vaddr;
	for (uint8_t i = 0; i < 4; i++)
	{
		if (get_user(check_byteptr + i) == -1)
		{
			sys_exit(-1);
		}
	}

	return ptr;
}

/* 
根据状态码退出
realized by rym
*/
void sys_exit(int status)
{
	thread_current()->exit_code = status;
	thread_exit();
}

/* 
写入文件，只完成了fd=1
realized by rym
*/
/* realized by zhy */
int sys_write(int fd, const void *buffer, unsigned fdbuffersize) {
    if (!is_valid_buffer(buffer, fdbuffersize))
        sys_exit(-1);

    /* 标准输入流 */
    if (fd == STD_IN) {
        sys_exit(-1);
    }
        /* 标准输出流 */
    else if (fd == STD_OUT) {
        putbuf(buffer, fdbuffersize);
        return fdbuffersize;
    }
        /* 读取文件内容 */
    else {
        struct file *file = get_file_by_fd(fd, NULL);

        if (file == NULL){
            sys_exit(-1);
						return 0;
				}
				
				acquire_lock_file();
        int t = file_write(file, buffer, fdbuffersize);
				release_lock_file();
				return t;
    }
}

void sys_halt(void) {
    shutdown_power_off();
}

pid_t sys_exec(const char *cmd_line) {
    return process_execute(cmd_line);
}

int sys_wait(pid_t pid) {
    return process_wait(pid);
}

/* realized by zhy */
bool sys_create(const char *filename, unsigned fileinitial_size) {
    /* 判断文件名是否合法 */
    if (!is_valid_string(filename))
        sys_exit(-1);

    /* 判断文件名是否非空 */
    if (strlen(filename) == 0)
        return 0;
		acquire_lock_file();
    bool flag = filesys_create(filename, fileinitial_size);
		release_lock_file();
		return flag;
}

/* realized by zhy */
bool sys_remove(const char *file) {
    if (!is_valid_string(file))
        sys_exit(-1);

		acquire_lock_file();
    bool flag = filesys_remove(file);
		release_lock_file();
		return flag;
}

/* realized by zhy */
int sys_open(const char *file) {
    struct thread *t = thread_current();

    /* 如果 fd 编号溢出了，则打开失败 */
    if (t->next_fd < 0)
        return -1;

    /* 如果文件名的地址没被映射，则直接退出 */
    if (!is_valid_string(file))
        sys_exit(-1);

    /* 申请空间存放打开文件列表的元素 */
    struct open_file_list_elem *e = (struct open_file_list_elem *) malloc(sizeof(struct open_file_list_elem));

		acquire_lock_file();
    /* 使用文件系统打开文件 */
    e->file = filesys_open(file);
		release_lock_file();

    /* 如果打开失败，则释放元素 */
    if (e->file == NULL) {
        free(e);
        return -1;
    }

    e->fd = t->next_fd++;
    list_push_back(&(t->open_file_list), &(e->elem));
    return e->fd;
}

/* realized by zhy */
int sys_filesize(int fd) {
    struct file *file = get_file_by_fd(fd, NULL);
    if (file == NULL)
        sys_exit(-1);

		acquire_lock_file();
    int len = file_length(file);
		release_lock_file();
		return len;
}
/* realized by zhy */
/* 返回读取到的内容的字节数 */
int sys_read(int fd, void *buffer, unsigned fdbuffersize) {
    if (!is_valid_buffer(buffer, fdbuffersize))
        sys_exit(-1);

    /* 标准输入流 */
    if (fd == STD_IN) {
        uint8_t *c = buffer;
        for (unsigned i = 0; i != fdbuffersize; ++i)
            *c++ = input_getc();
        return fdbuffersize;
    }
        /* 标准输出流 */
    else if (fd == STD_OUT) {
        sys_exit(-1);
    }
        /* 读取文件内容 */
    else {
        struct file *file = get_file_by_fd(fd, NULL);

        if (file == NULL)
            sys_exit(-1);

				acquire_lock_file();
        int t =  file_read(file, buffer, fdbuffersize);
				release_lock_file();
				return t;
    }
}

/* realized by hwh*/
void sys_seek(int fd, unsigned fdposition) {
    struct file *file = get_file_by_fd(fd, NULL);
    if (file == NULL)
        sys_exit(-1);

		acquire_lock_file();
    file_seek(file,fdposition);
		release_lock_file();
}

unsigned sys_tell(int fd) {
    struct file *file = get_file_by_fd(fd, NULL);
    if (file == NULL)
        sys_exit(-1);

		acquire_lock_file();
    file_tell(file);
		release_lock_file();
}

/* realized by zhy */
void sys_close(int fd) {
    struct list_elem *e;
    struct file *file = get_file_by_fd(fd, &e);

    /* 如果文件没被打开，以 -1 退出。 */
    if (file == NULL)
        sys_exit(-1);

    /* 关闭对应文件，移除列表项，释放列表元素。 */
		acquire_lock_file();
    file_close(file);
		release_lock_file();
    list_remove(e);
    free(list_entry(e, struct open_file_list_elem, elem));
}

/* add by zhy */
/* 判断字符串的内容是否合法 */
bool is_valid_string(const char *str) {
    char *ptr = str;
    struct thread *t = thread_current();

    /* 对字符串每个字符的地址都进行判断，直到结束或者是非法 */
    while (pagedir_get_page(t->pagedir, ptr) != NULL && *ptr)
        ptr++;

    /* 如果因为非法而终止 while，返回 0 */
    if (pagedir_get_page(t->pagedir, ptr) == NULL)
        return 0;
    return 1;
}
/* add by zhy */
/* 判断 buffer 到 buffer+size 的是否都处于合法用户空间中，是的话返回1 */
bool is_valid_buffer(const char *buffer, const unsigned size) {
    unsigned i = 0;
    char *ptr = buffer;
    struct thread *t = thread_current();

    while (i < size) {
        if (pagedir_get_page(t->pagedir, &ptr[i]) == NULL)
            return 0;
        i++;
    }
    return 1;
}
/* add by zhy */
/* 根据 fd 在打开文件列表中寻找对应的文件，文件不存在则返回 NULL 。
   如果e为NULL，则函数会用内部的list_elem，否则函数会使用参数提供的e，在函数返回的时候e会指向对应的list_elem，方便后续操作 */
struct file *get_file_by_fd(int fd, struct list_elem **e) {
    struct file *file = NULL;
    struct thread *t = thread_current();
    struct list_elem *le, **ee;

    /* 判断 e 是不是 NULL */
    ee = e == NULL ? &le : e;

    for (*ee = list_begin(&(t->open_file_list)); *ee != list_end(&(t->open_file_list)); *ee = list_next(*ee)) {
        struct open_file_list_elem *ofle = list_entry(*ee,
        struct open_file_list_elem, elem);
        if (fd == ofle->fd) {
            file = ofle->file;
            break;
        }
    }
    return file;
}

